package io.github.jsp.util;

import org.jctools.queues.MpscArrayQueue;
import org.jctools.queues.MpscChunkedArrayQueue;
import org.jctools.queues.MpscUnboundedArrayQueue;
import org.jctools.queues.atomic.MpscAtomicArrayQueue;
import org.jctools.queues.atomic.MpscChunkedAtomicArrayQueue;
import org.jctools.queues.atomic.MpscUnboundedAtomicArrayQueue;
import org.jctools.queues.atomic.SpscLinkedAtomicQueue;
import org.jctools.queues.unpadded.SpscLinkedUnpaddedQueue;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.Deque;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;


public final class PlatformDependent {


    private static final int MPSC_CHUNK_SIZE =  1024;
    private static final int MIN_MAX_MPSC_CAPACITY =  MPSC_CHUNK_SIZE * 2;
    public static final boolean USE_MPSC_CHUNKED_ARRAY_QUEUE;


    static {
        Object unsafe = null;

        try {
            final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            // We always want to try using Unsafe as the access still works on java9 as well and
            // we need it for out native-transports and many optimizations.
            unsafeField.setAccessible(true);

            // the unsafe instance
            unsafe = unsafeField.get(null);
        } catch (NoSuchFieldException | SecurityException
                 | IllegalAccessException | NoClassDefFoundError e) {

        }

        if (unsafe == null) {
            //logger.debug("org.jctools-core.MpscChunkedArrayQueue: unavailable");
            USE_MPSC_CHUNKED_ARRAY_QUEUE = false;
        } else {
            //logger.debug("org.jctools-core.MpscChunkedArrayQueue: available");
            USE_MPSC_CHUNKED_ARRAY_QUEUE = true;
        }
    }

   public static <T> Queue<T> newMpscQueue() {
        return USE_MPSC_CHUNKED_ARRAY_QUEUE ? new MpscUnboundedArrayQueue<>(MPSC_CHUNK_SIZE)
                : new MpscUnboundedAtomicArrayQueue<>(MPSC_CHUNK_SIZE);
    }

    public static <T> Queue<T> newChunkedMpscQueue(final int chunkSize, final int capacity) {
        return USE_MPSC_CHUNKED_ARRAY_QUEUE ? new MpscChunkedArrayQueue<>(chunkSize, capacity)
                : new MpscChunkedAtomicArrayQueue<>(chunkSize, capacity);
    }


    /**
     * Create a new {@link Queue} which is safe to use for single producer (one thread!) and a single
     * consumer (one thread!).
     */
    public static <T> Queue<T> newSpscQueue() {
        return USE_MPSC_CHUNKED_ARRAY_QUEUE ? new SpscLinkedUnpaddedQueue<>() : new SpscLinkedAtomicQueue<>();
    }



    /**
     * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
     * consumer (one thread!) with the given fixes {@code capacity}.
     */
    public static <T> Queue<T> newFixedMpscQueue(int capacity) {
        return USE_MPSC_CHUNKED_ARRAY_QUEUE ? new MpscArrayQueue<>(capacity) : new MpscAtomicArrayQueue<>(capacity);
    }

    public static <C> Deque<C> newConcurrentDeque() {
        return new ConcurrentLinkedDeque<>();
    }
}
